package com.brook.example.rpc;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.ServerSocket;
import java.net.Socket;
import lombok.extern.slf4j.Slf4j;

/**
 * 简单Rpc 框架
 * @author Shaojun Liu <liushaojun@maizijf.com>
 * @create 2018/3/2
 */
@Slf4j
public class RpcFramework {

  /**
   * 暴露服务
   * @param service 服务
   * @param port 端口号
   * @throws Exception
   */
  public static void export(final Object service, int port) throws Exception {
     if (service == null)
       throw new IllegalArgumentException("service instance == null");
     if (port <= 0 || port > 65535)
       throw new IllegalArgumentException("Invalid port " + port);
     System.out.println("Export service " + service.getClass().getName() + " on port " + port);
     ServerSocket server = new ServerSocket(port);
     for (int i = 0;i< 10 ;i++ ) {
      try {
        new Thread(() -> {
           try {
             try (Socket socket = server.accept()) {
               try (ObjectInputStream input = new ObjectInputStream(socket.getInputStream())) {
                 String methodName = input.readUTF();
                 Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
                 Object[] arguments = (Object[]) input.readObject();
                 ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                 try {
                   Method method = service.getClass().getMethod(methodName, parameterTypes);
                   Object result = method.invoke(service, arguments);
                   output.writeObject(result);

                 } catch (Throwable t) {
                   output.writeObject(t);

                 } finally {
                   output.close();

                 }

               }

             }

          } catch (Exception e) {
             e.printStackTrace();

          }

        }).start();

      } catch (Exception e) {
        log.error("Error ",e);
      }

    }

  }


  /**
   *
   * @param interfaceClass <T> 接口
   * @param host 地址
   * @param port 端口
   * @param <T> 引用服务接口泛型
   * @return T
   * @throws Exception
   */
  public static <T> T refer(final Class<T> interfaceClass, final String host, final int port)
      throws Exception {
     if (interfaceClass == null)
       throw new IllegalArgumentException("Interface class == null");
     if (!interfaceClass.isInterface())
       throw new IllegalArgumentException(
        "The " + interfaceClass.getName() + " must be interface class!");
     if (host == null || host.length() == 0)
       throw new IllegalArgumentException("Host == null!");
     if (port <= 0 || port > 65535)
       throw new IllegalArgumentException("Invalid port " + port);
     log.info(
        "Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
     return (T) Proxy
        .newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[]{interfaceClass},
            (proxy, method, arguments) -> {
              try (Socket socket = new Socket(host, port)) {
                try (ObjectOutputStream output = new ObjectOutputStream(
                    socket.getOutputStream())) {
                  output.writeUTF(method.getName());
                  output.writeObject(method.getParameterTypes());
                  output.writeObject(arguments);
                  try (ObjectInputStream input = new ObjectInputStream(socket.getInputStream())) {
                    Object result = input.readObject();
                    if (result instanceof Throwable) {
                      throw (Throwable) result;

                    }
                    return result;

                  }

                }

              }

            });

  }


}

